<?php
/**
 * filename: upload.php
 * @abstract 文件上传处理类
 *          
 * @copyright (c) 2012 www.cdvphp.com
 * @author 范家鹏 fanjiapeng@126.com
 * @version   3.0  -    2013.10.23
 */

//error_reporting(0); //屏蔽所有错误信息

define('UPLOAD_FILE_EMPTY', 		   101);  //上传的附件、文件为空
define('UPLOAD_FILE_ILLEGAL',  	   	   102);  //上传文件非法
define('UPLOAD_FILE_POSTFIX_ILLEGAL',  103);  //上传文件格式非法
define('UPLOAD_FILE_SIZE_ILLEGAL',     104);  //上传文件大小非法
define('UPLOAD_FILE_FAILURE',   	   105);  //上传文件失败
define('UPLOAD_FILE_SIZE_TOOSMALL',    106);  //图片像素太小
define('UPLOAD_FILE_LAYER',			   107);  //分割文件名宽度超出范围
define('UPLOAD_FILE_CREATE_DIR', 	   108);  //创建目录没有权限
define('UPLOAD_FILE_TMP', 	   		   109);  //可能文件临时目录没有权限


class class_upload {
	//默认配置
	public $_config = array (
			'str_path'		=> '',		   						   //上传物理地址前缀
			'str_filetype'  => 'image',						 	   //类型为图片, 如果为定制类型, 名称随意取
			
			'int_cutwidth'  => 0,								   //默认等比例缩微切割宽度, int_cutwidth大于0 + bool_cut等于TRUE生效
			'int_cutheight' => 0,								   //默认等比例缩微切割高度, int_cutheight大于0 + bool_cut等于TRUE生效
			'int_maxsize'   => 5120000,						       //上传最大值, 单位字节,bool_checksize等于TRUE生效
			'int_cutlayer'	=> 2,								   //目录分割, 文件名宽度, bool_autodir等于TRUE生效, 取值范围 2 ~ 32, 否则等于2

			'bool_cut'       => FALSE,							   //是否需要切图
			'bool_checktype' => TRUE,							   //是否检查图片格式
			'bool_checksize' => TRUE,							   //是否检查图片大小
			'bool_autodir'	 => TRUE,							   //默认开启目录自动分割, 只上传原图有效
			
			'arr_allowtype' => array('gif', 'jpeg', 'png', 'bmp'), //默认可以上传的图片类型
	);
	
	//getimagesize + $_FILES值
	public  $get_data = '';
	private $dir_sep = DIRECTORY_SEPARATOR;
	private $arr_all_type = array(1 => 'GIF', 2 => 'JPG', 3 => 'PNG', 4 => 'SWF', 5 => 'PSD', 6 => 'BMP', 
		7 => 'TIFF(intel byte order)', 8 => 'TIFF(motorola byte order)', 9 => 'JPC', 10 => 'JP2',
		11 => 'JPX', 12 => 'JB2', 13 => 'SWC', 14 => 'IFF', 15 => 'WBMP', 16 => 'XBM');
	
	//文件上传配置初始化
	public function __construct ($config = array()) {
		if (!$config || !is_array($config)) {
			return FALSE;
		}

		foreach ($this->_config as $k => $v) {
			isset($config[$k]) && $this->_config[$k] = $config[$k];
		}

		foreach($this->_config['arr_allowtype'] as $k => &$v) {
			$v = strtolower($v);
		}
		
		if(!$this->_config['str_path']) $this->_config['str_path'] = ROOT_PATH.'./data/upload';
		unset($config);
	}
	
	//文件原图上传
	public function put($arr_files = array()) {
		$this->get_data = & $arr_files;

		if(!isset($this->get_data['tmp_name']) || !isset($this->get_data['name']) || empty($this->get_data['tmp_name']) || empty($this->get_data['name'])) {
			$this->halt(UPLOAD_FILE_EMPTY);
		}

		if(!isset($this->get_data['error']) || intval($this->get_data['error']) !== 0) {
			$this->halt(UPLOAD_FILE_FAILURE);
		}
		
		if(isset($this->get_data['cut_layer']) && ($this->get_data['cut_layer'] > 32 || $this->get_data['cut_layer'] < 2)) {
			$this->halt(UPLOAD_FILE_FAILURE);
		}

		$this->get_data['tmp_name'] = realpath($this->get_data['tmp_name']);
		if(is_uploaded_file($this->get_data['tmp_name'])) {
			$this->check_safe();
			$this->create_new_path($this->_config['bool_autodir'], $this->_config['int_cutlayer']);
			$this->create_dir();

			//图片或者文件都可以上传
			return $this->do_upload() ? $this->get_data : UPLOAD_FILE_FAILURE;
		} else {
			return UPLOAD_FILE_TMP;
		}
	}

	/**
	 * 文件上传开始
	 *
	 * @param  <string> tmp_name
	 * @param  <string> path, new_path
	 * @return <boolean>
	 */
	private function do_upload() {
		if(move_uploaded_file($this->get_data['tmp_name'], $this->get_data['full_path'])) {
			return TRUE;
		}

		return FALSE;
	}
	
	/**
	 * 判断上传文件大小是否合法
	 *
	 * @param  <number> size文件大小值
	 * @return <boolean>
	 */
	private function check_size() {
		if($this->_config['bool_checksize'] && $this->get_data['size'] > $this->_config['int_maxsize']) {
			return FALSE;
		}

		return TRUE;
	}
	
	/**
	 * 取得文件名后缀
	 *
	 * @param  <string>  name
	 * @return <string>	返回小写后缀名称
	 */
	private function get_post_fix($file_name = '') {
		return strtolower(trim(substr(strrchr(($file_name ? $file_name : $this->get_data['name']), '.'), 1, 10)));
	}
	
	/**
	 * 检查文件是否为规定上传类型
	 *
	 * @param  <string> name
	 * @return <boolean>
	 */
	private function check_format() {
		if($this->_config['bool_checktype'] && !in_array($this->get_post_fix(), $this->_config['arr_allowtype'])) {
			return FALSE;
		}

		return TRUE;
	} 
	
	/**
	 * 创建分隔目录
	 *
	 * @param  <string> path配置目录, new_path完整目录
	 */
	private function create_dir() {
		if(!file_exists($path = dirname($this->get_data['full_path']))) {
			if(!@mkdir($path, 0777, TRUE)) {
				return UPLOAD_FILE_CREATE_DIR;
			}
		}
	}
	
	/**
	 * $auto_dir 如果为假, 目录分割功能关闭
	 *
	 * @param  <string> new_name, new_dir, new_path
	 * @return <string> new_dir, new_path
	 */
	private function create_new_path($auto_dir = TRUE, $cut_layer = 2) {
		$time = time();
		$this->get_data['new_name'] = md5($time . rand(1, 100)). '.' . $this->get_post_fix();
		$this->get_data['new_dir'] =  $this->dir_sep;

		if($auto_dir) {
			$this->get_data['new_dir'] =  date('Ym', $time). $this->dir_sep;
			$this->get_data['new_dir'] .= substr($this->get_data['new_name'], 0, $cut_layer). $this->dir_sep;
		}

		$this->get_data['full_path'] = $this->_config['str_path']. $this->dir_sep. $this->get_data['new_dir'] . $this->get_data['new_name'];
	}

	/**
	 * 图片有效性检测
	 *
	 * @param   <string> fullPath 完整全路径地址
	 * @return  <string> width图片宽度, height图片高度, img_type_number图片数字类型
	 */
	private function check_safe() {
		if(!$this->check_format()) {
			$this->halt(UPLOAD_FILE_POSTFIX_ILLEGAL);
		}

		if(!$this->check_size()) {
			$this->halt(UPLOAD_FILE_SIZE_ILLEGAL);
		}

		$this->get_image();
	}
	
	/**
	 * 取得图片的长、宽、类型
	 *
	 * @param   <string> fullPath 完整全路径地址
	 */
	private function get_image($fullpath = '') {
		if ($this->_config['str_filetype'] == 'image') {
			list($this->get_data['width'], $this->get_data['height'], $this->get_data['img_type_number']) = getimagesize($fullpath ? $fullpath : $this->get_data['tmp_name']);
			if(!isset($this->arr_all_type[$this->get_data['img_type_number']]) || !in_array(strtolower($this->arr_all_type[$this->get_data['img_type_number']]), $this->_config['arr_allowtype'])) {
				$this->halt(UPLOAD_FILE_POSTFIX_ILLEGAL);
			}
		}
	}
		
	/**
     * 生成等比例缩微图thumb
	 *
	 * @param  <string> $old_filename   原始文件名 /data/08b44de34eb6f5460f374f8bd418b9bd.gif
	 * @param  <string> $new_filename   新文件名   08b44de34eb6f5460f374f8bd418b9bd_thumb.gif, 只命名新图片下划线后面部分
	 * @param  <string> $dir_path	    把生成的新图片存放到另一个目录中, 如果为空则保存到原始文件名目录中(全路径)
	 *
	 * 注： 多切只需要传文件名即可, 不带后缀
	 * @return <boolean>   TRUE 成功, FALSE 失败
     */
	public function put_thumb($old_filename = '', $new_file_extension = 'thumb', $dir_path = '') {
		//$filepath = $old_filename ? $this->_config['str_path']. $this->dir_sep. $old_filename : $this->get_data['full_path'];
		$filepath = $old_filename ? $old_filename : $this->get_data['full_path'];

		if($filepath && $this->_config['str_filetype'] == 'image' && $this->_config['bool_cut'] && $this->_config['int_cutwidth'] && $this->_config['int_cutheight'] && @file_exists($filepath)) {

			if(!isset($this->get_data['width']) || !isset($this->get_data['height'])) {
				$this->get_image($filepath);
			}

			switch($this->get_data['img_type_number']) {
				case 1:
					$im = imagecreatefromgif($filepath);
				break;
				case 2:
					$im = imagecreatefromjpeg($filepath);
				break;
				case 3:
					$im = imagecreatefrompng($filepath);
				break;
			}

			if(!$im) {
				return FALSE;
			}

			$this->_config['int_cutwidth'] > $this->get_data['width'] && $this->_config['int_cutwidth'] = $this->get_data['width'];
			$this->_config['int_cutheight'] > $this->get_data['height'] && $this->_config['int_cutheight'] = $this->get_data['height'];
			
			$width = $this->_config['int_cutwidth']; $height = $this->_config['int_cutheight'];
			if ($this->_config['int_cutwidth'] && ($this->get_data['width'] < $this->get_data['height'])) {
    			$width = ($this->_config['int_cutheight'] / $this->get_data['height']) * $this->get_data['width'];
			} else {
    			$height = ($this->_config['int_cutwidth'] / $this->get_data['width']) * $this->get_data['height'];
			}

			if (function_exists("imagecreatetruecolor")) {
				$new = imagecreatetruecolor($width, $height);
				$this->get_data['img_type_number'] == 3 && $this->transparent($new);
				imagecopyresampled($new, $im, 0, 0, 0, 0, $width, $height, $this->get_data['width'], $this->get_data['height']);
			} else {
				$new = imagecreate($width, $height);
				$this->get_data['img_type_number'] == 3 && $this->transparent($new);
				imagecopyresized($new, $im, 0, 0, 0, 0, $width, $height, $this->get_data['width'], $this->get_data['height']);
			}

			//$new_filepath = $dir_path ? $dir_path. $this->dir_sep. $new_filename : trim(substr($this->get_data['full_path'], 0, @strrpos($this->get_data['full_path'], '.'))) . '_' . $new_filename;
			$new_filepath = ($dir_path ? $dir_path : trim(substr($filepath, 0, @strrpos($filepath, '.')))) . '_' . $new_file_extension;

			switch ($this->get_data['img_type_number']) {
				case 1:
					imagegif($new, $new_filepath. '.gif');
				break;
				case 2:
					imagejpeg($new, $new_filepath. '.'. $this->get_post_fix($filepath), 100);
				break;
				case 3:
					imagepng($new, $new_filepath. '.png');
				break;
			}

			imagedestroy($new); imagedestroy($im);
			unset($old_filename, $new_filename, $width, $height);
			return $new_filepath. '.'. $this->get_post_fix($filepath);
		}

		return FALSE;
	}

	/**
	 * PNG透明背景图片处理
	 *
	 * @param  <resource> new 资源
	 * @param  <int> transparent 如果分配失败则返回 FALSE
	 */	
	private function transparent($new) {
		$transparent = imagecolorallocatealpha($new , 0 , 0 , 0 , 127);
		imagealphablending($new , false);
		imagefill($new , 0 , 0 , $transparent);
		imagesavealpha($new , true);
	}

	//格式化错误输出
	private function halt($message) {
		echo $message;
		exit();
	}
}
